package com.kim.oauth.common.handle;

import com.kim.oauth.common.constants.EnableDynamicAutEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.web.FilterInvocation;

import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 客户端作用域投票
 */
@Slf4j
public class ClientScopeVoter implements AccessDecisionVoter<Object> {

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return null != configAttribute.getAttribute();
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

    @Override
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> collection) {
        // 默认弃权
        int result = ACCESS_ABSTAIN;
        log.info("----------------------客户端作用域投票------------------");
        Boolean flag = (Boolean) ((FilterInvocation) object).getRequest().getAttribute(EnableDynamicAutEnum.ENABLE_DYNAMIC_SCOPE_AUTH.getName());
        // 未启用客户端作用域投票则弃权或者此路径无需Scope校验直接弃权
        if (!flag || CollectionUtils.isEmpty(collection)) {
            log.info("未启用客户端作用域投票则弃权或者此路径无需Scope校验直接弃权");
            return result;
        }
        if (null == authentication) {
            log.error("用户认证信息不存在");
            return ACCESS_DENIED;
        }
        if (!(authentication instanceof OAuth2Authentication)) {
            log.error("非 OAuth2Authentication对象");
            return result;
        } else {
            OAuth2Authentication oauth2Authentication = (OAuth2Authentication) authentication;
            OAuth2Request clientAuthentication = oauth2Authentication.getOAuth2Request();
            // 授予的令牌作用域
            Set<String> scopes = clientAuthentication.getScope();
            //todo 后续可能会加入客户端权限校验
            Collection<String> stringCollection = collection
                    .stream()
                    .map(String::valueOf)
                    .filter(configAttribute -> configAttribute.contains(EnableDynamicAutEnum.ENABLE_DYNAMIC_SCOPE_AUTH.getPrefix()))
                    .map(s -> s.replaceFirst(EnableDynamicAutEnum.ENABLE_DYNAMIC_SCOPE_AUTH.getPrefix(), ""))
                    .collect(Collectors.toSet());
            // 如果包含全部所需要作用域(好像是一个一个校验)
            if (scopes.containsAll(stringCollection)) {
                log.info("包含全部所需要作用域");
                return ACCESS_GRANTED;
            }
        }
        // 否定
        return ACCESS_DENIED;
    }
}
